package com.demo.chapter3lambda;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

/**局部变量作用域：lambda表达式可以没有限制的捕获实例变量和静态变量，但局部变量必须显示的声明为final或事实上是final的。
 * 主要是出于线程安全的考虑，实例变量存储在堆上，可以线程间共享；局部变量保存在栈上
 * @author dell
 *
 */
public class LocalVariableScope {

	/**一个lambda表达式包括三个部分：
	一段代码
	参数
	自由变量的值，这里的“自由”指的是那些不是参数并且没有在代码中定义的变量。
	在我们的示例中，lambda表达式有两个自由变量，text和count。数据结构表示lambda表达式必须存储这两个变量的值，即“Hello”和20。
	我们可以说，这些值已经被lambda表达式捕获了（这是一个技术实现的细节。例如，你可以将一个lambda表达式转换为一个只含一个方法的对象，这样自由变量的值就会被复制到该对象的实例变量中）。
	注意：含有自由变量的代码块才被称之为“闭包（closure）”。在Java中，lambda表达式就是闭包。事实上，内部类一直都是闭包。Java8中为闭包赋予了更吸引人的语法。
	lambda表达式可以捕获闭合作用域中的变量值。在java中，为了确保被捕获的值是被良好定义的，需要遵守一个重要的约束。在lambda表达式中，被引用的变量的值不可以被更改。
	 * @param text
	 * @param count
	 */
	public static void repeatMessage(String text){
		int count = 10;
        Runnable r = () -> {
            for(int i = 0; i < count; i++){
            	//count--;        //错误，不能更改已捕获变量的值
                System.out.println(text);
                Thread.yield();
            }
        };
        new Thread(r).start();
    }

	public static void repeatMessage2(String text,int count){
		List<Integer> matches = new ArrayList<>();
        Runnable r = () -> {
            for(int i = 0; i < count; i++){
            	//count--;        //错误，不能更改已捕获变量的值
            	//注意matches是“有效final”的（一个有效的final变量被初始化后，就永远不会再被赋一个新值的变量）。在我们的示例中，matches总是引用同一个ArrayList对象，但是，这个对象是可变的，因此是线程不安全的 。如果多个线程同时调用add方法，结果将无法预测。
            	matches.add(1);
                System.out.println(text);
                Thread.yield();
            }
        };
        new Thread(r).start();
    }

	//lambda表达式的方法体与嵌套代码块有着相同的作用域。因此它也适用同样的命名冲突和屏蔽规则。在lambda表达式中不允许声明一个与局部变量同名的参数或者局部变量。
	public static void repeatMessage3(String text,int count){
		//String first = "dad1";
		Comparator<String> comp = (first, second) -> Integer.compare(first.length(),second.length());
    }

	//当你在lambda表达式中使用this关键字，你会引用创建该lambda表达式的方法的this参数，以下面的代码为例：
	//表达式this.toString()会调用LocalVariableScope对象的toString()方法，而不是Runnable实例的toString()方法。在lambda表达式中使用this，与在其他地方使用this没有什么不同。lambda表达式的作用域被嵌套在doWork()方法中，并且无论this位于方法的何处，其意义都是一样的。
	public void repeatMessage4(String text,int count){
		Runnable runner = () -> {System.out.println(this.toString());};
	}

	public static void main(String[] args) {
        repeatMessage("Hello");
    }

}
